﻿#pragma once
#include "XyNet/Channel/RpcChannel.h"
#include "XyNet/Common/Id.h"
#include <boost/asio.hpp>

namespace XyNet
{
	class RpcEndpoint;
	class RpcConnection;
	class RpcServiceManager;

	/// RPC套接字通道。负责建立连接并通过此连接通信
	class XYNET_API RpcSocketChannel
		: public RpcChannel, public enable_shared_from_this<RpcSocketChannel>
	{
	public:
		RpcSocketChannel(RpcEndpoint* endpoint,
			shared_ptr<boost::asio::ip::tcp::socket> socket);
		virtual ~RpcSocketChannel();

		/// 获取套接字通道id
		const Id& getId() const;

		/// 获取用户数据
		template <typename Type>
		Type getUserData() const
		{
			return any_cast<Type>(_userData);
		}
		/// 设置用户数据
		template <typename Type>
		void setUserData(const Type& userData)
		{
			_userData = userData;
		}
		/// 清理用户数据和服务管理器服务（用于用户登出，清除服务是防止非法使用服务）
		void clearUserDataAndService();

		/// 获取所属终端
		RpcEndpoint* getEndpoint();

		/// 获取套接字
		shared_ptr<boost::asio::ip::tcp::socket> getSocket();
		/// 获取服务管理器
		shared_ptr<RpcServiceManager> getServiceManager();

		/// 开始读写循环和事件处理
		void start();
		/// 停止读写循环和事件处理
		void stop();
		/// 是否连接完成
		bool isConnectComplete() const;

		/// 处理socket异常
		void handleSocketException(const boost::system::error_code& error);
		/// 处理socket通道异常
		void handleSocketChannelException(int errorCode);
		/// 处理连接数据（连接会调用此函数）
		void handleConnectionData(const Bytes& data);

		/// 是否存在缓存数据
		bool hasCacheDatas() const;
		/// 处理缓存数据（用于在主线程处理网络数据）
		void handleCacheDatas();
		/// 弹出缓存数据（跨语言交换数据用）
		vector<Bytes> popCacheDatas();

		/// 设置失败回调（一般是超时等原因调用）
		void setFailedCallback(const Id& messageId, int errorCode);

		/// 获取回调数量（测试回调是否都有处理）
		int getCallbackCount() const;
		/// 获取处理消息数量
		inline int64 getTotalHandleMessage() const { return _totalHandleMessage; }
		/// 获取总延时
		inline chrono::steady_clock::duration getTotalDelay() const { return _totalDelay; }

		/// 获取平均延时
		chrono::steady_clock::duration getAverageDelay() const;
		/// 获取最小延时
		inline chrono::steady_clock::duration getMinDelay() const { return _minDelay; }
		/// 获取最大延时
		inline chrono::steady_clock::duration getMaxDelay() const { return _maxDelay; }

		/// 获取最大每秒读入消息数量
		int getMaxReadMessageCountPerSecond() const;
		/// 获取最大每秒写入消息数量
		int getMaxWriteMessageCountPerSecond() const;
		/// 获取最大每秒读入字节
		int getMaxReadBytesPerSecond() const;
		/// 获取最大每秒写入字节
		int getMaxWriteBytesPerSecond() const;

		/// 执行方法
		virtual void callMethod(RpcMethod* method, shared_ptr<RpcCallback> callback) override;
		///// 发送数据
		//virtual void sendData(const Bytes& data) override;

	//protected:
		/// 读取（子类可能会对原始数据解包）
		virtual void read(const Bytes& data);
		/// 写入（子类可能会对原始数据包装）
		virtual void write(const Bytes& data);

	private:
		/// 添加缓存数据
		void addCacheData(const Bytes& data);
		/// 解析消息
		void parseMessage(const Bytes& data);
		/// 弹出回调
		shared_ptr<RpcCallback> popCallback(const Id& messageId);

		/// 获取连接（如果没有会创建一个）
		shared_ptr<RpcConnection> getConnection();

	private:
		/// 套接字通道id，构造时生成，不可修改
		const Id _id;
		/// 用户数据
		any _userData;

		/// 所属终端
		RpcEndpoint* _endpoint;
		/// 套接字
		shared_ptr<boost::asio::ip::tcp::socket> _socket;
		/// 负责字节流传输的连接
		shared_ptr<RpcConnection> _connection;
		/// 服务管理器（根据用户登录情况添加或删除服务）
		shared_ptr<RpcServiceManager> _serviceManager;

		/// 消息id创建器
		IdGenerator _messageIdGenerator;
		/// 回调参数映射对象
		SharedObject<map<Id, shared_ptr<RpcCallback>>> _idCallbackMapObject;

		/// 缓存数据对象
		SharedObject<vector<Bytes>> _cacheDatasObject;

		/// 处理消息数量
		int64 _totalHandleMessage;
		/// 总延时
		chrono::steady_clock::duration _totalDelay;
		/// 最小延时
		chrono::steady_clock::duration _minDelay;
		/// 最大延时
		chrono::steady_clock::duration _maxDelay;

		/// 套接字通道id生成器
		static IdGenerator _socketChannelIdGenerator;
	};
}

//////////////////////////////////////////////////////////////////////////

namespace XyNet
{
	extern "C"
	{
		/// 是否存在缓存数据
		XYNET_API bool RpcSocketChannelHasCacheDatas(
			RpcSocketChannel* socketChannel);
		/// 弹出缓存数据
		XYNET_API vector<Bytes>* RpcSocketChannelPopCacheDatas(
			RpcSocketChannel* socketChannel);

		/// 发送数据
		XYNET_API void RpcSocketChannelWriteData(
			RpcSocketChannel* socketChannel, Bytes* data);
	}
}